package com.lib.framework.network

import android.annotation.SuppressLint
import android.content.Context
import com.facebook.stetho.okhttp3.StethoInterceptor
import com.lib.framework.BuildConfig
import com.lib.framework.network.adapter.request.Request2CallAdapterFactory
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory


/**
 * Copyright (C), 2019-2020
 * FileName: NetworkManager
 * Author: Kevin
 * Date: 2020-01-06 15:05
 * Description: 网络请求管理器，在 createRetrofit() 之前可以进行 Retrofit 和 OkHttpClient 配置，
 *              如果在之后再进行配置，需要重新调用 createRetrofit()，生成新的 Retrofit 对象。
 * History:
 * <author> <time> <version> <desc>
 * 作者姓名 修改时间 版本号 描述
 */
class NetworkManager private constructor(val context: Context) {

    companion object {

        /**
         * OkHttpClient 配置的名称
         */
        const val OKHTTPCLIENT_CONFIG_NAME_LOG = "log" // 日志配置
        const val OKHTTPCLIENT_CONFIG_NAME_NETWORK = "network" // 网络配置

        /**
         * Retrofit 配置的名称
         */
        const val RETROFIT_CONFIG_NAME_BASEURL = "BaseUrl" // api域名
        const val RETROFIT_CONFIG_NAME_CLIENT = "Client" // client
        const val RETROFIT_CONFIG_NAME_CONVERTER = "Converter" // ConverterFactory
        const val RETROFIT_CONFIG_NAME_ADAPTER = "Adapter" // Adapter

        @Volatile
        @SuppressLint("StaticFieldLeak")
        private var INSTANCE: NetworkManager? = null

        fun getInstance(context: Context): NetworkManager {
            if (INSTANCE == null) {
                synchronized(NetworkManager::class) {
                    if (INSTANCE == null) {
                        INSTANCE = NetworkManager(context.applicationContext)
                    }
                }
            }
            return INSTANCE!!
        }
    }

    /**
     * api 域名
     */
    var mBaseUrl = ""

    /**
     * OkHttpClient 对象
     */
    var mOkHttpClient: OkHttpClient? = null

    /**
     * Retrofit 对象
     */
    var mRetrofit: Retrofit? = null

    /**
     * OkHttpClient 配置类
     */
    private val mOkHttpClientConfig = OkHttpClientConfig(context)

    /**
     * Retrofit 配置类
     */
    private val mRetrofitConfig = RetrofitConfig(context)

    /**
     * api 服务接口对象map，key 是接口类的canonicalName， valude 是接口类对象
     */
    private val mServiceMap = mutableMapOf<String, Any?>()

    init {
        ininOkHttpClientConfig()
        ininRetrofitConfig()
    }

    /**
     * 创建 Retrofit
     *
     * @param isReCreateOkHttpClient 是否重新创建 OkHttpClient
     */
    fun createRetrofit(isReCreateOkHttpClient: Boolean = false) {
        if (mOkHttpClient == null || isReCreateOkHttpClient) {
            createOkHttpClient()
        }

        mRetrofit = RetrofitFactory.createRetrofit(mRetrofitConfig)
    }

    /**
     * 创建 OkHttpClient
     */
    private fun createOkHttpClient() {
        mOkHttpClient = OkHttpClientFactory.createOkHttpClient(mOkHttpClientConfig)
    }

    /**
     * 初始化 OkHttpClient 配置类
     */
    private fun ininOkHttpClientConfig() {
        // 设置 log 配置
        addOkHttpClientConfig(OKHTTPCLIENT_CONFIG_NAME_LOG) { _, builder ->
            val logging = HttpLoggingInterceptor()
            logging.level = HttpLoggingInterceptor.Level.BODY
            builder.addInterceptor(logging)
        }

        // 设置 网络 配置
        if (BuildConfig.DEBUG) {
            addOkHttpClientConfig(OKHTTPCLIENT_CONFIG_NAME_NETWORK) { _, builder ->
                builder.addNetworkInterceptor(StethoInterceptor())
            }
        }
    }

    /**
     * 初始化 Retrofit 配置类
     */
    private fun ininRetrofitConfig() {
        // 设置 域名配置
        addRetrofitConfig(RETROFIT_CONFIG_NAME_BASEURL) { _, builder ->
            if (!mBaseUrl.startsWith("http")) return@addRetrofitConfig

            builder.baseUrl(mBaseUrl)
        }

        // 设置 client
        addRetrofitConfig(RETROFIT_CONFIG_NAME_CLIENT) { _, builder ->
            mOkHttpClient?.let {
                builder.client(it)
            }
        }

        // 设置 call 适配器
        addRetrofitConfig(RETROFIT_CONFIG_NAME_ADAPTER) { _, builder ->
            builder.addCallAdapterFactory(Request2CallAdapterFactory())
        }

        // 设置 结果转换器
        addRetrofitConfig(RETROFIT_CONFIG_NAME_CONVERTER) { _, builder ->
            builder.addConverterFactory(GsonConverterFactory.create())
        }
    }


    /**
     * 增加 OkHttpClient 配置接口，加入的配置放在列表头部，如果存在该配置名称的配置，就替换掉原有配置
     *
     * @param configName 配置名称（唯一），可用于移除配置
     * @param config 配置接口
     */
    fun addOkHttpClientConfig(
        configName: String,
        config: (context: Context, builder: OkHttpClient.Builder) -> Unit
    ) {
        mOkHttpClientConfig.addConfig(configName, config, 0)
    }

    /**
     * 移除 OkHttpClient 配置接口
     */
    fun removeOkHttpClientConfig(configName: String) {
        mOkHttpClientConfig.removeConfig(configName)
    }

    /**
     * 清空  OkHttpClient 配置接口
     */
    fun clearOkHttpClientConfig() {
        mOkHttpClientConfig.clear()
    }

    /**
     * 增加 RetrofitConfig 配置接口，如果存在该配置名称的配置，就替换掉原有配置
     *
     * @param configName 配置名称（唯一），可用于移除配置
     * @param config 配置接口
     */
    fun addRetrofitConfig(configName: String,
                          config: (context: Context, builder: Retrofit.Builder) -> Unit) {
        mRetrofitConfig.addConfig(configName, config)
    }

    /**
     * 移除 RetrofitConfig 配置接口
     */
    fun removeRetrofitConfig(configName: String) {
        mRetrofitConfig.removeConfig(configName)
    }

    /**
     * 清空 RetrofitConfig 配置接口
     */
    fun clearRetrofitConfig() {
        mRetrofitConfig.clear()
    }

    /**
     * 获取 api 所在的服务接口对象
     */
    fun <T> getService(service: Class<T>): T? {
        val key = service.canonicalName
        if (mServiceMap[key] != null) {
            val serviceObj = mServiceMap[key] as? T
            if (serviceObj != null) {
                return serviceObj
            }
        }

        if (mRetrofit == null) {
            createRetrofit()
        }

        val retrofit = mRetrofit ?: return null
        val serviceObj = retrofit.create(service)
        if (!key.isNullOrEmpty()) {
            mServiceMap[key] = serviceObj
        }
        return serviceObj
    }
}